﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.Text.RegularExpressions;

namespace MailRuWrapper.Server.PaymentService
{
    [ServiceContract]
    public interface IPaymentService
    {
        //<system.serviceModel>
        //  <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />

        //  <behaviors>
        //    <endpointBehaviors>
        //      <behavior name="webBehavior">
        //        <webHttp/>
        //      </behavior>
        //    </endpointBehaviors>
        //  </behaviors>

        //  <services>
        //    <service name="MailRuWrapper.Server.PaymentService" >
        //      <endpoint address="" binding="webHttpBinding"
        //        contract="MailRuWrapper.Server.PaymentService.IPaymentService"
        //        behaviorConfiguration="webBehavior"/>
        //    </service>
        //  </services>
        //</system.serviceModel>

        /// <summary>
        /// Пример ответа скрипта оказания услуг:
        ///{  
        /// "status" : "1", 
        /// "error_code" : "701", 
        ///}
        ///  *  status	 int	 Cтатус обработки услуги: status = 1 - услуга оказана успешно; status = 0 - услуга не оказана.
        ///     error_code	 int	 Код ошибки, если услуга не оказана (status=0). Если status=1, то error_code передавать не нужно.
        /// 
        /// * 701 - User not found - если приложение не смогло найти пользователя е с данным идентификатором для оказания услуги
        /// * 702 - Service not found - если услуга с данный идентификатором не существуем в вашем приложении
        /// * 703 - Incorrect price for given uid and service_id - если данная услуга для данного пользователя не могла быть оказана за указанную цену
        /// </summary>
        ///<param name="appId">Идентификатор приложения.</param>
        ///<param name="transactionId">идентификатор денежной транзакции. Каждый факт поступления денег от пользователя регистрируется как отдельная транзакция с уникальным идентификатором. 
        /// Вы можете фиксировать его для каждого факта оказания услуги пользователю, чтобы в принципе исключить возможность повторного оказания услуги по данной транзакции.</param>
        /// <param name="serviceId">Идентификатор услуги, который был передан разработчиком в вызове функции API по приему платежа.</param>
        /// <param name="uid">Идентификатор пользователя, который оплатил услугу.</param>
        /// <param name="sig">Подпись запроса(сигнатура), рассчитывается по аналогии с подписью запроса любого вызова API по защищенной схеме "сервер-сервер".</param>
        /// <param name="smsPrice">Возможные значения - 1,3,5. Номинал смс-платежа, который был указан при вызове функции payments.openDialog. Передается в том случае, если пользователь оплатил
        ///  услугу с помощью смс. Этот параметр можно использовать, например, для определения точного количества игровой валюты, которое нужно начислить данному пользователю, если пользователь сам 
        /// выбирал, сколько именно валюты он хочет купить.</param>
        /// <param name="otherPrice">Стоимость услуги в копейках, которая была указана при вызове функции payments.openDialog. Передается в том случае, если пользователь оплатил услугу любым способом,
        ///  кроме смс. Этот параметр можно использовать, например, для определения точного количества игровой валюты, которое нужно начислить данному пользователю, если пользователь сам выбирал, сколько
        ///  именно валюты он хочет купить.</param>
        /// <param name="debug">Флаг, определяющий режим отладки. Если debug=1, то приложение должно учитывать, что это тестовый вызов. **Внимание**: тестовый вызов должен обрабатываться точно также, 
        /// как и обычный вызов. Тестовый вызов может быть осуществлен только разработчиком приложения из экрана настроек приема платежей или администратором Платформы.</param>
        /// <returns></returns>
        [OperationContract]
        [WebGet(UriTemplate = "/*?app_id={appId}&transaction_id={transactionId}&service_id={serviceId}&uid={uid}&sig={sig}&sms_price={smsPrice}&other_price={otherPrice}&debug={debug}", BodyStyle = WebMessageBodyStyle.Bare)]
        System.IO.Stream InvokePaymentService(int appId, int transactionId, int serviceId, string uid, string sig, int smsPrice, int otherPrice, int debug);
    }

    /// <summary>
    /// 
    /// </summary>
    public class PaymentServiceHelper
    {
        /// <summary>
        /// Получить json форматированый ответ для биллинг системы
        /// </summary>
        /// <param name="status">Статус обработки запроса</param>
        /// <param name="errorCode">Код ошибки при обработке запроса</param>
        /// <returns></returns>
        public static System.IO.Stream GetResponse(Status status, ErrorCode errorCode = ErrorCode.None)
        {
            
            if ((status == Status.Success & errorCode != ErrorCode.None) | (status != Status.Success & errorCode == ErrorCode.None))
                throw new FormatException("Значения status и errorCode не соответствуют друг другу");

            string _result;
            if (errorCode != ErrorCode.None)
            {
                _result = @"{  
 ""status"" : """ + status.GetAttributes<DefaultValueAttribute>().Single().Value +
                          @""", 
 ""error_code"" : """ +
                          errorCode.GetAttributes<DefaultValueAttribute>().Single().Value + @""" 
}";
            }
            else
            {
                _result = @"{  
 ""status"" : """ + status.GetAttributes<DefaultValueAttribute>().Single().Value +
                          @""" 
}";
            }

            if (WebOperationContext.Current != null)
                WebOperationContext.Current.OutgoingResponse.ContentType = "text/plain";

            return new MemoryStream(Encoding.UTF8.GetBytes(_result));
        }

        ///<summary>
        /// Проветить подпись payment запроса
        ///</summary>
        ///<param name="serverKey">Секретный ключ</param>
        ///<returns></returns>
        ///<exception cref="InvalidOperationException"></exception>
        public static bool CheckSIG(string serverKey)
        {
            if (WebOperationContext.Current == null)
                throw new InvalidOperationException("WebOperationContext неопределен");
            var _query = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.RequestUri.Query;

            var _regEx = new Regex(@"\??(([\w,\d]+)=([\w\d]+))\&?", 0);


            var _p = new List<String>();

            var _sig = "";

            foreach (Match _matche in _regEx.Matches(_query))
            {
                if (_matche.Groups[2].Value == "sig")
                {
                    _sig = _matche.Groups[3].Value;
                    continue;
                }
                _p.Add(_matche.Groups[1].Value);
            }

            if (String.IsNullOrEmpty(_sig))
                throw new InvalidOperationException("QueryString имеет неправильный формат");

            return _sig == _p.ComputeSIG(serverKey);
        }
    }

    /// <summary>
    /// Стаутс обработки запроса
    /// </summary>
    public enum Status
    {
        /// <summary>
        /// Услуга на данный момент не оказана, но будет оказана позднее
        /// </summary>
        [DefaultValue("0")]
        Error = 0,
        /// <summary>
        /// Услуга оказана успешно
        /// </summary>
        [DefaultValue("1")]
        Success = 1,
        /// <summary>
        /// Услуга не может быть оказана
        /// </summary>
        [DefaultValue("2")]
        Fail = 2
    }

    /// <summary>
    /// Код ошибки
    /// </summary>
    public enum ErrorCode
    {
        /// <summary>
        /// Ошибки нет
        /// </summary>
        None,
        /// <summary>
        /// User not found - если приложение не смогло найти пользователя  с данным идентификатором для оказания услуги
        /// </summary>
        [DefaultValue("701")]
        UserNotFound,

        /// <summary>
        /// Service not found - если услуга с данный идентификатором не существуем в вашем приложении
        /// </summary>
        [DefaultValue("702")]
        ServiceNotFound,

        /// <summary>
        /// Incorrect price for given uid and service_id - если данная услуга для данного пользователя не могла быть оказана за указанную цену
        /// </summary>
        [DefaultValue("703")]
        IncorrectPrice

    }
}


